/*
 * Copyright (c) 2010, VeRSI Consortium
 *   (Victorian eResearch Strategic Initiative, Australia)
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the VeRSI, the VeRSI Consortium members, nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package versi.vitro.oaiserviceprovider.service.validation.helper;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.List;

import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamSource;

import org.apache.log4j.Logger;
import org.w3c.dom.Document;

import versi.vitro.oaiserviceprovider.service.validation.api.ValidationHelper;
import versi.vitro.oaiserviceprovider.xml.XmlDomBuilder;
import versi.vitro.oaiserviceprovider.xml.XmlDomBuilderFactory;
import versi.vitro.oaiserviceprovider.xml.XmlDomBuilderFactoryImpl;
import versi.vitro.oaiserviceprovider.xml.XmlSchema;
import versi.vitro.oaiserviceprovider.xml.XmlSchemaFactory;
import versi.vitro.oaiserviceprovider.xml.XmlSchemaFactoryImpl;
import versi.vitro.oaiserviceprovider.xml.XmlTransformerFactory;
import versi.vitro.oaiserviceprovider.xml.XmlTransformerFactoryImpl;
import versi.vitro.oaiserviceprovider.xml.XmlValidator;

/**
 * Service implementing {@link ValidationHelper}.
 */
public class ValidationHelperImpl implements ValidationHelper {

    /** log4j instance. */
    static final Logger LOGGER = Logger.getLogger(ValidationHelperImpl.class);

    /** DOM object builder Factory.. */
    private XmlDomBuilderFactory domBuilderFactory;
   
    /** XML Schema Factory. */
    private XmlSchemaFactory xmlSchemaFactory;

    
    /**
     * Default Constructor.
     */
    public ValidationHelperImpl() {
	this(new XmlTransformerFactoryImpl(), new XmlDomBuilderFactoryImpl(), new XmlSchemaFactoryImpl());
    }

    /**
     * Constructor.
     */
    public ValidationHelperImpl(XmlTransformerFactory transformerFactory, XmlDomBuilderFactory domFactory,
	    XmlSchemaFactory xmlSchemaFactory) {
	super();
	this.domBuilderFactory = domFactory;
	this.xmlSchemaFactory = xmlSchemaFactory;
    }

    /** {@inheritDoc} */
    public List<Source> addFiletoSchemas(final File schemaFile, final List<Source> source) {

	Source schemaSource = createSchemaSource(schemaFile);
	schemaSource.setSystemId(schemaFile.getAbsolutePath());
	source.add(schemaSource);
	return source;
    }

    /**
     * 
     * @param schemaFile
     * @return
     */
    protected Source createSchemaSource(File schemaFile) {
	try {
	    return new StreamSource(new FileInputStream(schemaFile));
	} catch (FileNotFoundException fileNotFoundException) {
	    String message = "Schema file not loaded: " + schemaFile.getAbsolutePath();
	    LOGGER.fatal(message, fileNotFoundException);
	    throw new RuntimeException(message, fileNotFoundException);
	}
    }

    /** {@inheritDoc} */
    public boolean validateAgainstSchema(byte[] sourceByte, Source[] schemas) {

	InputStream inputStream = createXmlDocumentInputStream(sourceByte);
	XmlDomBuilder domBuilder = domBuilderFactory.create();
	Document dom = domBuilder.parse(inputStream);
	Source source = createSource(dom);

	Result result = createResult();

	XmlSchema schema = xmlSchemaFactory.create(schemas);
	XmlValidator validator = schema.createValidator();

	boolean isValid = validator.validate(source, result);

	if (LOGGER.isDebugEnabled()) {
	    DOMResult domResult = (DOMResult) result;
	    Document augmented = (Document) domResult.getNode();
	    LOGGER.debug(augmented.toString());
	}
	return isValid;
    }

    /**
     * 
     * @return Result A new DOM Result.
     */
    protected Result createResult() {
	return new DOMResult();
    }

    /**
     * 
     * @param dom
     * @return Source A new DOM Source.
     */
    protected Source createSource(Document dom) {
	return new DOMSource(dom);
    }

    /**
     * 
     * @param sourceByte
     * @return InputSteam 
     */
    protected InputStream createXmlDocumentInputStream(byte[] sourceByte) {
	InputStream inputStream = new BufferedInputStream(new ByteArrayInputStream(sourceByte));
	return inputStream;
    }
}
